package com.xjm_cloud_admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xjm_cloud_admin.adpater.UserVoAdapter;
import com.xjm_cloud_admin.config.security.context.AuthenticationContextHolder;
import com.xjm_cloud_admin.constant.CacheConstants;
import com.xjm_cloud_admin.constant.Constants;
import com.xjm_cloud_admin.constant.HttpStatus;
import com.xjm_cloud_admin.domain.XjmUser;
import com.xjm_cloud_admin.domain.vo.req.RegisterReqVo;
import com.xjm_cloud_admin.domain.vo.req.UserInfoReqVo;
import com.xjm_cloud_admin.domain.vo.resp.UserInfoRespVo;
import com.xjm_cloud_admin.exception.CustomException;
import com.xjm_cloud_admin.exception.ServerException;
import com.xjm_cloud_admin.mapper.XjmUserMapper;
import com.xjm_cloud_admin.redis.RedisCache;
import com.xjm_cloud_admin.security.pojo.LoginUser;
import com.xjm_cloud_admin.security.service.TokenService;
import com.xjm_cloud_admin.service.XjmUserService;
import com.xjm_cloud_admin.util.CommonUtils;
import com.xjm_cloud_admin.util.R;
import com.xjm_cloud_admin.util.SecurityUtils;
import com.xjm_cloud_admin.domain.vo.req.UserLoginReqVo;
import com.xjm_cloud_admin.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 用户表(XjmUser)表服务实现类
 *
 * @author 浪漫写尽歌谱
 * @since 2024-04-22 16:23:27
 */
@Service("xjmUserService")
@Slf4j
public class XjmUserServiceImpl extends ServiceImpl<XjmUserMapper, XjmUser> implements XjmUserService {

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private TokenService tokenService;

    @Resource
    private RedisCache redisCache;

    @Value("${sxg.profile}")
    private String filePath;

    @Value("${sxg.ip}")
    private String ip;

    @Value("${server.port}")
    private String port;

    /**
     * @param xjmUser 用户信息
     * @return 返回用户token
     */
    @Override
    public R login(UserLoginReqVo xjmUser) {
        // 获取邮箱前缀并校验
        String email = verifyEmail(xjmUser);
        // 用户验证
        try {
            // 构建认证上下文对象
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(email, xjmUser.getPassword());
            // 设置用户认证上下文
            AuthenticationContextHolder.setContext(usernamePasswordAuthenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);

            // 验证通过后获取上下文中存储的用户信息
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();

            // 生成token
            String token = tokenService.createToken(loginUser);
            return R.success().put("token", token);
        } catch (AuthenticationException e) {
            if (e instanceof BadCredentialsException) {
                throw new CustomException(HttpStatus.SHOW_TOAST, "密码错误");
            } else {
                throw new ServerException(e.getMessage());
            }
        } finally {
            // 清除用户认证上下文
            AuthenticationContextHolder.clearContext();
        }
    }

    private String verifyEmail(UserLoginReqVo xjmUser) {
        try {
            return CommonUtils.getEmailPrefix(xjmUser.getUsername());
        } catch (Exception e) {
            throw new CustomException(HttpStatus.SHOW_TOAST, "不支持的邮箱格式");
        }
    }

    /**
     * @param username 用户邮箱前缀
     * @return 返回用户信息
     */
    @Override
    public XjmUser selectUserByUserName(Long username) {
        return baseMapper.selectUserByUserName(username);
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public R info() {
        // 通过上下文获取当前用户信息
        XjmUser xjmUser = SecurityUtils.getLoginUser().getXjmUser();
        UserInfoRespVo userInfoRespVo = UserVoAdapter.getUserInfoRespVo(xjmUser);
        return R.success(userInfoRespVo);
    }

    /**
     * 更新用户信息
     *
     * @param vo 用户信息
     * @return 更新结果
     */
    @Override
    public R update(UserInfoReqVo vo) {
        XjmUser xjmUser = new XjmUser();
        // 更新用户信息
        xjmUser.setNickname(vo.getNickname());
        xjmUser.setUid(SecurityUtils.getLoginUser().getXjmUser().getUid());
        this.baseMapper.updateById(xjmUser);
        // 更新上下文的用户信息并且更新缓存
        LoginUser loginUser = SecurityUtils.getLoginUser();
        XjmUser user = loginUser.getXjmUser();
        user.setNickname(vo.getNickname());
        loginUser.setXjmUser(user);
        tokenService.setLoginUser(loginUser);
        return R.success();
    }

    /**
     * 修改头像
     *
     * @param file 文件
     */
    @Override
    public R avatar(MultipartFile file) {
        try {
            // 获取用户信息
            Long userId = SecurityUtils.getUserId();
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 获取文件后缀
            assert fileName != null;
            String suffix = CommonUtils.getFileNameSuffix(fileName);
            // 生成新的文件名
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            String format = simpleDateFormat.format(new Date());
            String newFileName = format + "-" + userId + suffix;
            // 获取文件存储路径
            String name = "avatar" + File.separator + newFileName;
            String outPath = filePath + File.separator + name;
            File outFile = new File(outPath);
            outFile.getParentFile().mkdirs();
            file.transferTo(outFile);
            // 更新用户头像
            XjmUser xjmUser = new XjmUser();
            String previewPath = ip + "pro-api" + Constants.RESOURCE_PREFIX + Constants.SEPARATOR + name;
            xjmUser.setAvatar(previewPath);
            xjmUser.setUid(userId);
            baseMapper.updateById(xjmUser);
            // 更新上下文的用户信息并且更新缓存
            LoginUser loginUser = SecurityUtils.getLoginUser();
            XjmUser user = loginUser.getXjmUser();
            user.setAvatar(previewPath);
            loginUser.setXjmUser(user);
            tokenService.setLoginUser(loginUser);
            return R.success();
        } catch (IOException e) {
            log.error("头像上传失败", e);
            return R.error("头像上传失败");
        }
    }

    /**
     * 注册
     *
     * @param vo 注册信息
     * @return
     */
    @Override
    public R register(RegisterReqVo vo) {
        String qq = null;
        try {
            qq = vo.getEmail().substring(0, vo.getEmail().indexOf("@"));
        } catch (Exception e) {
            return R.error("邮箱格式错误");
        }

        Long username = null;
        try {
            username = Long.valueOf(qq);
        } catch (NumberFormatException e) {
            return R.error("目前只支持前缀为数字的邮箱，例如qq邮箱");
        }

        Object object = redisCache.getCacheObject(CacheConstants.MAIL_CODE_KEY + qq);
        if (object == null) {
            return R.error("验证码已过期");
        }

        if (!object.equals(vo.getCode())) {
            return R.error("验证码错误");
        }

        if (StringUtils.isNotEmpty(qq)) {
            XjmUser user = baseMapper.selectOne(new QueryWrapper<XjmUser>().lambda().select(XjmUser::getEmailPrefix).eq(XjmUser::getEmailPrefix, qq));
            if (StringUtils.isNotNull(user)) {
                return R.error("该账户已注册");
            }
        }

        redisCache.deleteObject(CacheConstants.MAIL_CODE_KEY + qq);

        // 注册用户信息
        XjmUser user = new XjmUser();
        user.setEmail(vo.getEmail());
        user.setNickname(vo.getEmail());
        user.setEmailPrefix(username);
        user.setPassword(SecurityUtils.encryptPassword(vo.getPassword()));
        user.setGender("2");
        user.setStatus("0");
        user.setCloudSize(524288000L);
        user.setSurplusCloudSize(524288000L);
        baseMapper.insert(user);
        return R.success();
    }

    public XjmUser getByUserId(Long userId) {
        String key = CacheConstants.USER_INFO_CACHE_PREFIX + userId;
        XjmUser user = redisCache.getPaserCacheObject(key, XjmUser.class);
        if (user == null) {
            user = baseMapper.selectById(userId);
            redisCache.setCacheObject(key, user);
            redisCache.expire(key, 30, TimeUnit.MINUTES);
        }
        return user;
    }
}